#ifndef xpack_math_expe
#define xpack_math_expe
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::math_expe::inc
#include"define/base_type.hpp"
#include"define/inf.hpp"
#include"macro/xexport.hpp"
#include"math/div.hpp"
#include"meta/is_same.hpp"
#include"utils/mfxx.hpp"
#pragma pop_macro("xuser")

namespace mixc::math_expe{
    constexpr uxx mask_low          = 32 - 1;
    constexpr uxx shift             = 5;
    constexpr f64 f64_lut_ee0_ee31  []  = {1.000000000000000000e+00, 2.718281828459045091e+00, 7.389056098930649441e+00, 2.008553692318766454e+01, 5.459815003314422746e+01, 1.484131591025765640e+02, 4.034287934927349939e+02, 1.096633158428458191e+03, 2.980957987041727006e+03, 8.103083927575380129e+03, 2.202646579480670480e+04, 5.987414171519778343e+04, 1.627547914190038169e+05, 4.424133920089201974e+05, 1.202604284164775882e+06, 3.269017372472108031e+06, 8.886110520507865075e+06, 2.415495275357527638e+07, 6.565996913733044828e+07, 1.784823009631870805e+08, 4.851651954097897619e+08, 1.318815734483213224e+09, 3.584912846131587367e+09, 9.744803446248890680e+09, 2.648912212984343848e+10, 7.200489933738577679e+10, 1.957296094288384936e+11, 5.320482406017978527e+11, 1.446257064291473020e+12, 3.931334297144036011e+12, 1.068647458152444510e+13, 2.904884966524737734e+13, };
    constexpr f64 f64_lut_ee32_ee704[]  = {1.000000000000000000e+00, 7.896296018268056078e+13, 6.235149080811595660e+27, 4.923458286012033263e+41, 3.887708405994568627e+55, 3.069849640644216345e+69, 2.424044149410054833e+83, 1.914097016509259280e+97, 1.511427665004082965e+111, 1.193468025307192564e+125, 9.423976816163424229e+138, 7.441451060972171904e+152, 5.875990038289116240e+166, 4.639855674272511094e+180, 3.663767388609647591e+194, 2.893019184253871395e+208, 2.284413586539694437e+222, 1.803840590747083853e+236, 1.424365927430649631e+250, 1.124721500132732559e+264, 8.881133903158571014e+277, 7.012806227721646385e+291, 5.537519389284386289e+305 };

    constexpr f32 f32_lut_ee0_ee88  []  = {
        1.00000000e+00f, 2.71828183e+00f, 7.38905610e+00f, 2.00855369e+01f, 5.45981500e+01f, 1.48413159e+02f, 4.03428793e+02f, 1.09663316e+03f, 2.98095799e+03f, 8.10308393e+03f, 2.20264658e+04f, 5.98741417e+04f, 1.62754791e+05f, 4.42413392e+05f, 1.20260428e+06f, 3.26901737e+06f, 
        8.88611052e+06f, 2.41549528e+07f, 6.56599691e+07f, 1.78482301e+08f, 4.85165195e+08f, 1.31881573e+09f, 3.58491285e+09f, 9.74480345e+09f, 2.64891221e+10f, 7.20048993e+10f, 1.95729609e+11f, 5.32048241e+11f, 1.44625706e+12f, 3.93133430e+12f, 1.06864746e+13f, 2.90488497e+13f, 
        7.89629602e+13f, 2.14643580e+14f, 5.83461743e+14f, 1.58601345e+15f, 4.31123155e+15f, 1.17191424e+16f, 3.18559318e+16f, 8.65934004e+16f, 2.35385267e+17f, 6.39843494e+17f, 1.73927494e+18f, 4.72783947e+18f, 1.28516001e+19f, 3.49342711e+19f, 9.49611942e+19f, 2.58131289e+20f, 
        7.01673591e+20f, 1.90734657e+21f, 5.18470553e+21f, 1.40934908e+22f, 3.83100800e+22f, 1.04137594e+23f, 2.83075330e+23f, 7.69478527e+23f, 2.09165950e+24f, 5.68572000e+24f, 1.54553894e+25f, 4.20121040e+25f, 1.14200739e+26f, 3.10429794e+26f, 8.43835667e+26f, 2.29378316e+27f, 
        6.23514908e+27f, 1.69488924e+28f, 4.60718663e+28f, 1.25236317e+29f, 3.40427605e+29f, 9.25378173e+29f, 2.51543867e+30f, 6.83767123e+30f, 1.85867175e+31f, 5.05239363e+31f, 1.37338298e+32f, 3.73324200e+32f, 1.01480039e+33f, 2.75851345e+33f, 7.49841700e+33f, 2.03828107e+34f, 
        5.54062238e+34f, 1.50609731e+35f, 4.09399696e+35f, 1.11286375e+36f, 3.02507732e+36f, 8.22301271e+36f, 2.23524660e+37f, 6.07603023e+37f, 1.65163625e+38f, 
    };
}

namespace mixc::math_expe::origin{
    template<class f64_t>
    requires(inc::is_same<f64_t, f64>)
    inline f64 expe_unsafe(uxx x){
        return f64_lut_ee0_ee31[x & mask_low] * f64_lut_ee32_ee704[x >> shift];
    }

    template<class f64_t>
    requires(inc::is_same<f64_t, f64>)
    inline f64 expe(uxx x){
        if (x >= sizeof(f64_lut_ee0_ee31) / sizeof(f64_lut_ee0_ee31[0]) * sizeof(f64_lut_ee32_ee704) / sizeof(f64_lut_ee32_ee704[0])){
            return inc::inf_pos;
        }
        return expe_unsafe<f64>(x);
    }

    template<class f32_t>
    requires(inc::is_same<f32_t, f32>)
    inline f32 expe_unsafe(uxx x){
        return f32_lut_ee0_ee88[x];
    }

    template<class f32_t>
    requires(inc::is_same<f32_t, f32>)
    inline f32 expe(uxx x){
        if (x >= sizeof(f32_lut_ee0_ee88) / sizeof(f32_lut_ee0_ee88[0])){
            return f32(inc::inf_pos);
        }
        return expe_unsafe<f32>(x);
    }

    extern f64 expe_unsafe(f64 x);

    inline f64 expe(f64 x){
        if (x > 700/*max exp*/){
            return inc::inf_pos;
        }
        if (x < -700/*min exp*/){
            return 0;
        }
        else{
            return expe_unsafe(x);
        }
    }
}

#endif

xexport_space(mixc::math_expe::origin)